
package ija.xls.notation;

import ija.model.basis.Position;
import ija.xls.notation.types.GameOperation;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

/**
 * Trida reprezentuje tah hry dama. Umoznuje pohyby, skoky, viceskoky, nasazeni
 * damy pescem.
 * @author Radek Necas, xnecas18
 */
public class GameMove implements Iterable<DeskCoordinate>, Serializable {
    
    protected List <DeskCoordinate> positions;
    protected GameOperation operation;
    protected String txt;
    
    
    /**
     * Konstruktor vytvori prazdny novy tah
     */
    public GameMove() {
        positions = new ArrayList<>();
        operation = GameOperation.NOP;
        txt = new String();
    }
    
    /**
     * Konstruktor vytvori novy tah a naplni jej hodnotami. Vyuziva se pri
     * klasickem pohybu ci skoku start - cil
     * @param startPosition pocatecni pozice
     * @param op operace
     * @param endPosition cilova pozice
     */
    public GameMove(DeskCoordinate startPosition, GameOperation op, DeskCoordinate endPosition) {
        positions = new ArrayList<>();
        positions.add(startPosition);
        positions.add(endPosition);
        operation = op;
        txt = new String();
    }
    
    /**
     * Konstruktor vytvori novy tah a naplni jej hodnotami. Vyuziva se pri
     * klasickem pohybu ci skoku start - cil
     * 
     * @param description popis tahu v txt
     * @param startPosition pocatecni pozice
     * @param op operace
     * @param endPosition cilova pozice 
     */
    public GameMove(String description, DeskCoordinate startPosition, GameOperation op, DeskCoordinate endPosition) {
        positions = new ArrayList<>();
        positions.add(startPosition);
        positions.add(endPosition);
        operation = op;
        txt = description;
    }
    
    /**
     * Konstruktor vytvori novy tah a naplni jej hodnotami. Vyuziva se pri
     * klasickem pohybu ci skoku start - cil
     * @param startPosition pocatecni pozice
     * @param op operace
     * @param endPosition cilova pozice
     */
    public GameMove(Position startPosition, GameOperation op, Position endPosition) {
        this(new DeskCoordinate(startPosition.getColumnChar(), startPosition.getRowNumber()),
                op,
                new DeskCoordinate(endPosition.getColumnChar(), endPosition.getRowNumber()));
    }
    
    /**
     * Konstruktor vytvori novy tah a naplni jej hodnotami. Vyuziva se pri
     * klasickem pohybu ci skoku start - cil
     * 
     * @param description popis tahu v txt
     * @param startPosition pocatecni pozice
     * @param op operace
     * @param endPosition cilova pozice 
     */
    public GameMove(String description, Position startPosition, GameOperation op, Position endPosition) {
        this(description,
                new DeskCoordinate(startPosition.getColumnChar(), startPosition.getRowNumber()),
                op,
                new DeskCoordinate(endPosition.getColumnChar(), endPosition.getRowNumber()));
    }
    
    /**
     * Metoda nastavi txt popis tahu
     * @param description textovy popis tahu
     */
    public void setDescription(String description) {
        txt = description;
    }
    
    /**
     * Metoda dalsi pozici pohybu
     * @param p pridavana pozice
     */
    public void addPosition(DeskCoordinate p) {
        positions.add(p);
    }
    
    /**
     * Metoda dalsi pozici pohybu
     * @param p pridavana pozice
     */
    public void addPosition(Position p) {
        this.addPosition(new DeskCoordinate(p.getColumnChar(), p.getRowNumber()));
    }
    
    /**
     * Metoda prida vsechny pozice pohybu. Neprovadi se hloubkova kopie, tzn.
     * jsou zkopirovany odkazy na objekty.
     * @param positions 
     */
    public void addPositions(List<DeskCoordinate> positions) {
        this.positions.addAll(positions);
    }
    
    /**
     * Metoda nastavi operaci pro tah
     * @param op operace (typ) tahu
     */
    public void setOperation(GameOperation op) {
        operation = op;
    }
    
    /**
     * 
     * @return typ tahu
     */
    public GameOperation getOperation() {
        return operation;
    }
    
    /**
     * 
     * @return pocet pozci pres ktere je tah veden
     */
    public int getPositionCount() {
        return positions.size();
    }
    
    /**
     * 
     * @return seznam vsech pozic
     */
    public List<DeskCoordinate> getPositions() {
        return this.positions;
    }
    
    /**
     * 
     * @return prvni pozici (startovni)
     */
    public DeskCoordinate getFirstPosition() {
        return positions.get(0);
    }
    
    /**
     * 
     * @return posledni pozici (cilovou)
     */
    public DeskCoordinate getLastPosition() {
        return positions.get(positions.size() - 1);
    }
    
    /**
     * 
     * @param index index pozice
     * @return pozice na danem indexu
     */
    public DeskCoordinate getPosition(int index) {
        if (index < 0 || index >= positions.size())
            return null;
        
        return positions.get(index);
    }

    /**
     * Test zda je seznam pozic prazdny
     * @return tru pokud je seznam pozic prazdny, jinak false
     */
    public boolean isEmpty() {
        if(operation == GameOperation.NOP && positions.isEmpty())
            return true;
        
        return false;
    }
    
    /**
     * Metoda vraci novy GameMove ve kterem jsou tahy revertovany. Napr. pokud
     * byl tah veden: a1-a2-a3 metoda vrati novy GameMove s pozicemi a3-a2-a1 a
     * se stejnou operaci. Txt popis tahu je taky vytvoren.
     * @return reverzni GameMove
     */
    public GameMove revert() {
        if(this.positions.isEmpty()) {
            return null;
        }
        
        // Vytvoreni reverzniho seznamu pozic
        ArrayList<DeskCoordinate> reverseList = new ArrayList<>(positions);
        Collections.reverse(reverseList);
        
        // Separator pro textovy popis tahu
        String separator;
        if(this.getOperation() == GameOperation.MOVE || this.getOperation() == GameOperation.MOVESET) {
            separator = "-";
        }
        else if (this.getOperation() == GameOperation.JUMP || this.getOperation() == GameOperation.JUMPSET) {
            separator = "x";
        }
        else {
            separator = "NOP";
        }
        
        // Ziskani textoveho popisu tahu
        String text = new String();
        Iterator<DeskCoordinate> it = reverseList.iterator();
        DeskCoordinate coord;
        while(it.hasNext()) {
            text += it.next().toString();
            if(it.hasNext()) {
                text += separator;
            }
        }
        
        // Naplneni a vraceni hodnot
        GameMove gm = new GameMove();
        gm.setPositions(reverseList);
        gm.setOperation(this.getOperation());
        gm.setDescription(text);
        return gm;
    }
    
    @Override
    public Iterator<DeskCoordinate> iterator() {
        return positions.iterator();
    }
    
    @Override
    public String toString() {
        if(txt.isEmpty()) {
            String s = new String();
            Iterator<DeskCoordinate> it = positions.iterator();
            DeskCoordinate pos;
            while(it.hasNext()) {
                pos = it.next();
                s += pos.toString();
                if(it.hasNext()) {
                    s += operation.toString();
                }
            }

            return s;
        }
        
        return txt;
    }
    
    /**
     * Metoda nastavi pozice na novou hodnotu. Nekopiruji se hodnoty uklada se odkaz.
     * @param list seznam pozic tahu
     */
    private void setPositions(ArrayList<DeskCoordinate> list) {
        this.positions = list;
    }
}
